﻿using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Milan.Deploy
{
	/// <summary>
	/// This custom system of deployment, makes it possible
	/// to install software, activate, check for updates...
	/// A download directory contains 
	/// http://company.com/download/productname/vXXX_XXX_XXX_XXX
	/// Under this directory, contains several files.
	/// latest.txt - The official release version of the product.
	/// beta.txt - An official beta release.
	/// alpha.txt - The latest uploaded version.
	/// Inside of each version directory contains another file:
	/// package.xml - The installation package.
	/// </summary
	public partial class Client
	{
		/// <summary>
		/// Returns true if a new release is remotely available.
		/// But this is more complicated than it sounds.  Checking for a new version means that it can be downloaded and passes an internal test.
		/// 1. First download the release type version file and compare to see if it is later than the version we have.
		/// 2. Create the directory for this particular version and download all the files to this directory
		/// 3. Run the self-test.  If succeeds, then mark it as a new version.
		/// </summary>
		public bool IsNewVersionAvailable()
		{
			bool isNewVersionAvailable = false;
			Uri currentUrl = null;
			Version remoteVersion = null;
			foreach (Uri url in UrlList)
			{
				try
				{
					remoteVersion = GetLatestVersionInfo(url);
					if (remoteVersion > Settings.ProductVersion)
					{
						isNewVersionAvailable = true;
						currentUrl = url;
						break;
					}
				}
				catch(Exception)
				{
				}
			}
			// Perform download and test
			if (currentUrl != null && remoteVersion!=null)
			{
				try
				{
					string destinationDirectory = Path.Combine(Settings.ProductFolder,FormatVersionDirectory(remoteVersion));
					DownloadVersionFiles(currentUrl,remoteVersion,destinationDirectory);
					//this.TestCommand = File.ReadAllText(Path.Combine(destinationDirectory, "selftest.txt"));
					//isNewVersionAvailable = Test();
				}
				catch (Exception)
				{
					isNewVersionAvailable = false;
				}
			}
			return isNewVersionAvailable;
		}

		/// <summary>
		/// This function checks if we should force update this program and if so we
		/// will download a new version and then restart this program with the given parameters.
		/// </summary>
		/// <returns></returns>
		public void ForceUpdateThisProgram()
		{
			if (UpgradeNow())
			{
				Process.Start(Environment.CommandLine);
				Environment.Exit(-1);
			}
		}

		/// <summary>
		/// Returns information about the latest version for the given type
		/// </summary>
		/// <param name="releaseType">Release type</param>
		/// <param name="productName">Official product name</param>
		/// <param name="downloadUrl">Download URL</param>
		/// <returns>null on failure, Version on success</returns>
		public Version GetLatestVersionInfo(Uri downloadUrl)
		{
			Version version = null;
			string textVersion = null;
			try
			{
				var url = GetReleaseTextFileUrl(downloadUrl,ReleaseType.Last);
				textVersion = Milan.Net.Download.GetTextFile(url);
			}
			catch (System.Net.WebException)
			{
				// IO-Exception
			}
			if (textVersion!=null)
				version = Version.Parse(textVersion);
			return version;
		}
		/// <summary>
		/// Get the latest remote version and URI corresponding to the latest version.
		/// This checks all the URIs.
		/// </summary>
		/// <param name="remoteVersion"></param>
		/// <param name="downloadUri">This is the URL where the latest version is located</param>
		/// <returns>true if they can download the version info and false if not (such as unreachable server)</returns>
		public bool GetLatestVersionInfo(out Version remoteVersion, out Uri downloadUri)
		{
			bool returns = false;
			remoteVersion = new Version(0, 0);
			downloadUri = null;
			foreach (Uri uri in this.m_urlList)
			{
				Version version = GetLatestVersionInfo(uri);
				if (version > remoteVersion)
				{
					returns = true;
					remoteVersion = version;
					downloadUri = uri;
				}
			}
			return returns;
		}
		/// <summary>
		/// Gets the text of the files.txt file for the given version
		/// </summary>
		/// <param name="downloadUrl"></param>
		/// <param name="version"></param>
		/// <returns></returns>
		private string GetVersionFileListFile(Uri downloadUrl, Version version)
		{
			Uri textFile = new Uri(FormatVersionUri(downloadUrl, version), "files.txt");
			string fileText = Milan.Net.Download.GetTextFile(textFile);
			return fileText;
		}
		/// <summary>
		/// Gets the version file list
		/// </summary>
		/// <param name="downloadUrl"></param>
		/// <param name="version"></param>
		/// <returns></returns>
		private string[] GetVersionFileList(Uri downloadUrl, Version version)
		{
			string text = GetVersionFileListFile(downloadUrl, version);
			string[] lines = text.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
			return lines;
		}
		/// <summary>
		/// Downloads the latest version from the given URL.
		/// </summary>
		/// <param name="downloadUrl">The download URL</param>
		/// <returns>Exception</returns>
		private bool DownloadVersionFiles(Uri downloadUrl, Version remoteVersion, string targetDirectory)
		{
bool returns = false;
			try
			{
				string[] fileList = GetVersionFileList(downloadUrl, remoteVersion);
				Uri baseVersionUri = FormatVersionUri(downloadUrl, remoteVersion);
				string versionTargetDirectory = System.IO.Path.Combine(targetDirectory, FormatVersionDirectory(remoteVersion));
				foreach (string file in fileList)
				{
					if (!String.IsNullOrEmpty(file))
					{
						var fileUri = new Uri(baseVersionUri, file);
						string destinationFile = System.IO.Path.Combine(versionTargetDirectory, file.Replace('/', '\\'));
						string destinationDirectory = Path.GetDirectoryName(destinationFile);
						Milan.Net.Download.Copy(fileUri, destinationFile);
						// Files with .deploy extensions must be renamed (and later decompressed.)
						if (destinationFile.EndsWith(".deploy"))
						{
							string renameTo = destinationFile.Substring(0, destinationFile.Length - 7);
							if (File.Exists(renameTo))
								File.Delete(renameTo);
							Milan.ZLib.DecompressFile(destinationFile, renameTo);
						}
					}
				}
				// If successful, then mark this as the latest downloaded version
				Settings.ProductMachine.SetValue("LastDownloadedVersion", remoteVersion.ToString());
				returns = true;
			}
			catch (Exception)
			{
			}
			return returns;
		}
		public void Download( Version version, string productFilesDirectory, Deploy.ReleaseType releaseType )
		{
			foreach (var uri in m_urlList)
			{
				try
				{
					Version remoteVersion = GetLatestVersionInfo(uri);
					if (version == remoteVersion)
					{
						DownloadVersionFiles(uri, version, productFilesDirectory);
					}
				}
				catch (Exception)
				{ }
			}
		}
		public void DownloadLatest()
		{
			Version remoteVersion = new Version(0,0);
			Uri lastUri = null;
			foreach (var uri in m_urlList)
			{
				try
				{
					Version version = GetLatestVersionInfo(uri);
					if (version > remoteVersion)
					{
						remoteVersion = version;
						lastUri = uri;
					}
				}
				catch (Exception)
				{}
			}
		}
	}
}
